package yixin.utils.bazi;

import com.nlf.calendar.EightChar;
import com.nlf.calendar.JieQi;
import com.nlf.calendar.Lunar;
import com.nlf.calendar.Solar;
import com.nlf.calendar.eightchar.*;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import yixin.utils.bazi.utils.BaZiShenShaUtil;
import yixin.utils.CommonUtil;
import yixin.utils.DateUtil;

import java.util.*;

/**
 * 八字
 *
 * @author 善待
 */
@Getter
public class BaZi {

    /**
     * 姓名
     */
    private String name;
    /**
     * 性别（男。女）
     */
    private String sex;
    /**
     * 乾造\坤造
     */
    private String zao;
    /**
     * 占事
     */
    private String occupy;

    /**
     * 公历日期
     */
    private Solar solar;
    /**
     * 农历日期
     */
    private Lunar lunar;

    /**
     * 公历日期
     */
    private String solarStr;
    /**
     * 农历日期
     */
    private String lunarStr;

    /**
     * 公历日期
     */
    private Date solarDate;
    /**
     * 农历日期
     */
    private Date lunarDate;

    /**
     * 星期
     */
    private String week;
    /**
     * 季节
     */
    private String season;
    /**
     * 生肖
     */
    private String shengXiao;
    /**
     * 星座
     */
    private String xingZuo;
    /**
     * 月相
     */
    private String yueXiang;
    /**
     * 月将
     */
    private String yueJiang;
    /**
     * 月将神
     */
    private String yueJiangShen;
    /**
     * 五不遇时
     */
    private String wuBuYuShi;

    /**
     * 八字
     */
    private List<String> baZi;
    /**
     * 八字五行
     */
    private List<String> baZiWuXing;
    /**
     * 八字空亡
     */
    private List<String> baZiKongWang;
    /**
     * 八字纳音
     */
    private List<String> baZiNaYin;

    /**
     * 年干
     */
    private String yearGan;
    /**
     * 月干
     */
    private String monthGan;
    /**
     * 日干
     */
    private String dayGan;
    /**
     * 时干
     */
    private String hourGan;

    /**
     * 年干支主星
     */
    private String yearGanZhiZhuXing;
    /**
     * 月干支主星
     */
    private String monthGanZhiZhuXing;
    /**
     * 日干支主星
     */
    private String dayGanZhiZhuXing;
    /**
     * 时干支主星
     */
    private String hourGanZhiZhuXing;

    /**
     * 年支
     */
    private String yearZhi;
    /**
     * 月支
     */
    private String monthZhi;
    /**
     * 日支
     */
    private String dayZhi;
    /**
     * 时支
     */
    private String hourZhi;

    /**
     * 年干支
     */
    private String yearGanZhi;
    /**
     * 月干支
     */
    private String monthGanZhi;
    /**
     * 日干支
     */
    private String dayGanZhi;
    /**
     * 时干支
     */
    private String hourGanZhi;

    /**
     * 年干五行
     */
    private String yearGanWuXing;
    /**
     * 月干五行
     */
    private String monthGanWuXing;
    /**
     * 日干五行
     */
    private String dayGanWuXing;
    /**
     * 时干五行
     */
    private String hourGanWuXing;

    /**
     * 年支五行
     */
    private String yearZhiWuXing;
    /**
     * 月支五行
     */
    private String monthZhiWuXing;
    /**
     * 日支五行
     */
    private String dayZhiWuXing;
    /**
     * 时支五行
     */
    private String hourZhiWuXing;

    /**
     * 年干支五行
     */
    private String yearGanZhiWuXing;
    /**
     * 月干支五行
     */
    private String monthGanZhiWuXing;
    /**
     * 日干支五行
     */
    private String dayGanZhiWuXing;
    /**
     * 时干支五行
     */
    private String hourGanZhiWuXing;

    /**
     * 年支藏干
     */
    private List<String> yearZhiCangGan;
    /**
     * 月支藏干
     */
    private List<String> monthZhiCangGan;
    /**
     * 日支藏干
     */
    private List<String> dayZhiCangGan;
    /**
     * 时支藏干
     */
    private List<String> hourZhiCangGan;

    /**
     * 年干支副星
     */
    private List<String> yearGanZhiFuXing;
    /**
     * 月干支副星
     */
    private List<String> monthGanZhiFuXing;
    /**
     * 日干支副星
     */
    private List<String> dayGanZhiFuXing;
    /**
     * 时干支副星
     */
    private List<String> hourGanZhiFuXing;

    /**
     * 年干支自坐
     */
    private String yearGanZhiZiZuo;
    /**
     * 月干支自坐
     */
    private String monthGanZhiZiZuo;
    /**
     * 日干支自坐
     */
    private String dayGanZhiZiZuo;
    /**
     * 时干支自坐
     */
    private String hourGanZhiZiZuo;

    /**
     * 年干支星运
     */
    private String yearGanZhiXingYun;
    /**
     * 月干支星运
     */
    private String monthGanZhiXingYun;
    /**
     * 日干支星运
     */
    private String dayGanZhiXingYun;
    /**
     * 时干支星运
     */
    private String hourGanZhiXingYun;

    /**
     * 年干支纳音
     */
    private String yearGanZhiNaYin;
    /**
     * 月干支纳音
     */
    private String monthGanZhiNaYin;
    /**
     * 日干支纳音
     */
    private String dayGanZhiNaYin;
    /**
     * 时干支纳音
     */
    private String hourGanZhiNaYin;

    /**
     * 年干支空亡
     */
    private String yearGanZhiKongWang;
    /**
     * 月干支空亡
     */
    private String monthGanZhiKongWang;
    /**
     * 日干支空亡
     */
    private String dayGanZhiKongWang;
    /**
     * 时干支空亡
     */
    private String hourGanZhiKongWang;

    /**
     * 年干支神煞
     */
    private List<String> yearGanZhiShenSha;
    /**
     * 月干支神煞
     */
    private List<String> monthGanZhiShenSha;
    /**
     * 日干支神煞
     */
    private List<String> dayGanZhiShenSha;
    /**
     * 时干支神煞
     */
    private List<String> hourGanZhiShenSha;
    /**
     * 小儿关煞
     */
    private List<String> xiaoErGuanSha;

    /**
     * 五行木数量（天干）
     */
    private int ganMuCount;
    /**
     * 五行火数量（天干）
     */
    private int ganHuoCount;
    /**
     * 五行土数量（天干）
     */
    private int ganTuCount;
    /**
     * 五行金数量（天干）
     */
    private int ganJinCount;
    /**
     * 五行水数量（天干）
     */
    private int ganShuiCount;

    /**
     * 五行木数量（地支）
     */
    private int zhiMuCount;
    /**
     * 五行火数量（地支）
     */
    private int zhiHuoCount;
    /**
     * 五行土数量（地支）
     */
    private int zhiTuCount;
    /**
     * 五行金数量（地支）
     */
    private int zhiJinCount;
    /**
     * 五行水数量（地支）
     */
    private int zhiShuiCount;

    /**
     * 五行木数量（藏干）
     */
    private int cangGanMuCount;
    /**
     * 五行火数量（藏干）
     */
    private int cangGanHuoCount;
    /**
     * 五行土数量（藏干）
     */
    private int cangGanTuCount;
    /**
     * 五行金数量（藏干）
     */
    private int cangGanJinCount;
    /**
     * 五行水数量（藏干）
     */
    private int cangGanShuiCount;

    /**
     * 天干留意
     */
    private List<String> tianGanLiuYi;
    /**
     * 地支留意
     */
    private List<String> diZhiLiuYi;

    /**
     * 人元司令分野
     */
    private String renYuan;

    /**
     * 出生节气
     */
    private String birthSolarTerms;

    /**
     * 上一节
     */
    private String prevJie;
    /**
     * 上一节日期
     */
    private String prevJieDateStr;
    /**
     * 距上一节天数
     */
    private int prevJieDayNumber;

    /**
     * 下一节
     */
    private String nextJie;
    /**
     * 下一节日期
     */
    private String nextJieDateStr;
    /**
     * 距下一节天数
     */
    private int nextJieDayNumber;

    /**
     * 星宿
     */
    private String xingXiu;
    /**
     * 星宿吉凶
     */
    private String xiuJiXiong;
    /**
     * 星宿吉凶歌诀
     */
    private String xiuJiXiongGeJue;

    /**
     * 胎元
     */
    private String taiYuan;
    /**
     * 胎息
     */
    private String taiXi;
    /**
     * 命宫
     */
    private String mingGong;
    /**
     * 身宫
     */
    private String shenGong;

    /**
     * 命
     */
    private String ming;
    /**
     * 命卦
     */
    private String mingGua;
    /**
     * 命卦基本分析
     */
    private String mingGuaJiBen;
    /**
     * 命卦风水分析
     */
    private String mingGuaFengShui;

    /**
     * 五行缺失
     */
    private List<String> wuXingQueShi;
    /**
     * 五行数量
     */
    private List<String> wuXingCount;
    /**
     * 五行旺衰
     */
    private List<String> wuXingWangShuai;

    /**
     * 身体强弱
     */
    private String shenTiQiangRuo;
    /**
     * 喜用神
     */
    private List<String> xiYongShen;
    /**
     * 喜用神方位
     */
    private List<String> xiYongShenFangWei;

    /**
     * 骨重
     */
    private String guZhong;
    /**
     * 骨重信息
     */
    private String guZhongPiZhu;

    /**
     * 日柱论命
     */
    private String dayZhuLunMing;

    /**
     * 姻缘分析
     */
    private String yinYuanFenXi;
    /**
     * 五行分析
     */
    private String wuXingFenXi;

    /**
     * 大运
     */
    private List<List<String>> daYun;
    /**
     * 流年
     */
    private List<List<String>> liuNian;

    /**
     * 正财运年份、年龄、干支
     */
    private List<List<String>> zhengCaiYun;
    /**
     * 偏财运年份、年龄、干支
     */
    private List<List<String>> pianCaiYun;
    /**
     * 正桃花年份、年龄、干支
     */
    private List<List<String>> zhengTaoHua;
    /**
     * 偏桃花年份、年龄、干支
     */
    private List<List<String>> pianTaoHua;

    /**
     * 起运
     */
    private String qiYun;
    /**
     * 起运日期
     */
    private String qiYunDateStr;
    /**
     * 交运
     */
    private String jiaoYun;



    /**
     * 八字
     */
    protected EightChar eightChar;
    /**
     * 运
     */
    protected Yun yun;
    /**
     * 小运持续的年数
     */
    protected int xiaoYunYearSum;

//****************************************************************************************************************************************************

    /**
     * 使用默认设置初始化（默认使用当前公历日期）
     */
    public BaZi() {
        core(new BaZiSetting());
    }

    /**
     * 使用公历日期初始化
     *
     * @param date 公历日期
     */
    public BaZi(Date date) {
        core(new BaZiSetting(date));
    }

    /**
     * 使用日期、日期类型初始化
     *
     * @param date     日期
     * @param dateType 日期类型（0:公历。1:农历）
     */
    public BaZi(Date date, int dateType) {
        core(new BaZiSetting(date, dateType));
    }

    /**
     * 使用公历年月日时分秒初始化
     *
     * @param year   公历年
     * @param month  公历月
     * @param day    公历日
     * @param hour   公历时
     * @param minute 公历分
     * @param second 公历秒
     */
    public BaZi(int year, int month, int day, int hour, int minute, int second) {
        core(new BaZiSetting(year, month, day, hour, minute, second));
    }

    /**
     * 使用年月日时分秒、日期类型初始化
     *
     * @param year     年
     * @param month    月
     * @param day      日
     * @param hour     时
     * @param minute   分
     * @param second   秒
     * @param dateType 日期类型（0:公历。1:农历）
     */
    public BaZi(int year, int month, int day, int hour, int minute, int second, int dateType) {
        core(new BaZiSetting(year, month, day, hour, minute, second, dateType));
    }

    /**
     * 使用自定义设置初始化
     *
     * @param setting 八字-设置
     */
    public BaZi(BaZiSetting setting) {
        core(setting);
    }

//====================================================================================================================================================

    /**
     * 获取并设置数据（★核心）
     *
     * @param setting 八字-设置
     */
    private void core(BaZiSetting setting) {

        // 初始化数据
        initialize(setting); // 初始化自定义数据
        initializeEightCharYun(setting); // 初始化外部类

        // 计算数据
        other(setting); // 计算星期、季节、生肖、星座、月相、月将、月将神、五不遇时
        baZi(); // 计算八字
        baZiWuXing(); // 计算八字五行
        baZiNaYin(); // 计算八字纳音
        baZiKongWang(); // 计算八字旬空
        zhuXing(); // 计算主星
        diZhiCangGan(); // 计算地支藏干
        fuXing(); // 计算副星
        ziZuo(); // 计算自坐
        xingYun(); // 计算星运
        shenSha(); // 计算常用神煞
        tianGanLiuYi(); // 计算天干留意
        diZhiLiuYi(); // 计算地支留意
        jieQi(); // 计算上一节及日期、下一节及日期、出生节气
        renYuan(setting); // 计算人元司令分野
        xingXiu(); // 计算星宿、星宿吉凶、星宿吉凶歌诀
        taiGong(); // 计算胎元、胎息、命宫、身宫
        mingGua(); // 计算命、命卦、命卦信息
        wuXingQueShi(); // 计算五行缺失
        wuXingCount(); // 计算五行数量
        wuXingWangShuai(); // 计算五行旺衰
        shenTiQiangRuo(); // 计算身体强弱
        xiYongShen(); // 计算喜用神
        xiYongShenFangWei(); // 计算喜用神方位
        guZhong(); // 计算骨重
        dayZhuLunMing(); // 计算日柱论命
        yinYuanFenXi(); // 计算姻缘分析
        wuXingFenXi(); // 计算五行分析
        DaYun[] daYun = daYun();// 计算大运
        liuNian(daYun);// 计算流年
        caiYun(); // 计算财运年份、年龄、干支、合化
        taoHua(); // 计算桃花年份、年龄、干支、合化
        qiYunJiaoYun(); // 计算起运、交运

    }

    /**
     * 初始化自定义数据
     *
     * @param setting 八字-设置
     */
    private void initialize(BaZiSetting setting) {

        // 1、设置基础信息
        this.name = StringUtils.isBlank(setting.getName()) ? "缘主" : setting.getName(); // 姓名
        this.occupy = StringUtils.isBlank(setting.getOccupy()) ? "未知" : setting.getOccupy(); // 占事
        this.sex = setting.getSex() == 0 ? "女" : "男"; // 性别
        this.zao = setting.getSex() == 0 ? "坤造" : "乾造"; // 乾造\坤造

        // 2.1、判断日期类型并返回公历日期、农历日期
        Map<String, Object> dateTypeMap = BaZiUtil.isDateType(setting);
        // 2.2、设置日期数据
        this.solar = (Solar) dateTypeMap.get("solar"); // 公历日期
        this.lunar = (Lunar) dateTypeMap.get("lunar"); // 农历日期
        this.solarStr = (String) dateTypeMap.get("solarStr"); // 公历日期
        this.lunarStr = (String) dateTypeMap.get("lunarStr"); // 农历日期
        this.solarDate = (Date) dateTypeMap.get("solarDate"); // 公历日期
        this.lunarDate = (Date) dateTypeMap.get("lunarDate"); // 农历日期

        // 3.1、判断干支设置并返回干支
        Map<String, List<String>> ganZhiMap = BaZiUtil.isGanZhi(setting, getSolar().getLunar());
        // 3.2、设置年干支
        List<String> yearGanZhi = ganZhiMap.get("yearGanZhi");
        this.yearGan = yearGanZhi.get(0); // 年干
        this.yearZhi = yearGanZhi.get(1); // 年支
        this.yearGanZhi = yearGanZhi.get(2); // 年干支
        // 3.3、设置月干支
        List<String> monthGanZhi = ganZhiMap.get("monthGanZhi");
        this.monthGan = monthGanZhi.get(0); // 月干
        this.monthZhi = monthGanZhi.get(1); // 月支
        this.monthGanZhi = monthGanZhi.get(2); // 月干支
        // 3.4、设置日干支
        List<String> dayGanZhi = ganZhiMap.get("dayGanZhi");
        this.dayGan = dayGanZhi.get(0); // 日干
        this.dayZhi = dayGanZhi.get(1); // 日支
        this.dayGanZhi = dayGanZhi.get(2); // 日干支
        // 3.5、设置时干支
        List<String> hourGanZhi = ganZhiMap.get("hourGanZhi");
        this.hourGan = hourGanZhi.get(0); // 时干
        this.hourZhi = hourGanZhi.get(1); // 时支
        this.hourGanZhi = hourGanZhi.get(2); // 时干支

    }

    /**
     * 初始化外部类
     *
     * @param setting 八字-设置
     */
    private void initializeEightCharYun(BaZiSetting setting) {

        EightChar eightChar = getLunar().getEightChar();
        eightChar.setSect(setting.getDayGanZhiSet()); // 日干支设置，同子时流派（0:晚子时日柱按明天。1:晚子时日柱按当天）
        Yun yun = eightChar.getYun(setting.getSex(), setting.getQiYunLiuPai() + 1);
        this.eightChar = eightChar; // 八字
        this.yun = yun; // 运

    }

    /**
     * 计算星期、季节、生肖、星座、月相、月将、月将神、五不遇时
     *
     * @param setting 八字-设置（基础）
     */
    private void other(BaZiSetting setting) {

        // 1、计算星期
        this.week = "周" + getLunar().getWeekInChinese();

        // 2、计算季节
        this.season = getLunar().getSeason();

        // 3、计算生肖
        if (setting.getYearGanZhiSet() == 0) {
            this.shengXiao = getLunar().getYearShengXiao(); // 以正月初一起算
        } else if (setting.getYearGanZhiSet() == 1) {
            this.shengXiao = getLunar().getYearShengXiaoByLiChun(); // 以立春当天起算
        } else if (setting.getYearGanZhiSet() == 2) {
            this.shengXiao = getLunar().getYearShengXiaoExact(); // 以立春交接时刻起算
        }

        // 4、计算星座
        this.xingZuo = getSolar().getXingZuo() + "座";

        // 5、计算月相、月将、月将神
        this.yueXiang = getLunar().getYueXiang(); // 月相
        this.yueJiang = BaZiMap.YUE_JIANG.get(getMonthZhi()).get(0); // 月将
        this.yueJiangShen = BaZiMap.YUE_JIANG.get(getMonthZhi()).get(1); // 月将神

        // 6、五不遇时
        this.wuBuYuShi = getHourGanZhi().equals(BaZiMap.WU_BU_YU_SHI.get(getDayGan())) ? "五不遇时" : "";

    }

    /**
     * 计算八字
     */
    private void baZi() {

        this.baZi = Arrays.asList(getYearGanZhi(), getMonthGanZhi(), getDayGanZhi(), getHourGanZhi()); // 八字

    }

    /**
     * 计算八字五行
     */
    private void baZiWuXing() {

        Map<String, String> tianGanWuXingMap = BaZiMap.TIAN_GAN_WU_XING; // 天干五行（天干为键）
        Map<String, String> diZhiWuXingMap = BaZiMap.DI_ZHI_WU_XING; // 地支五行（地支为键）

        this.yearGanWuXing = tianGanWuXingMap.get(getYearGan());   // 年干五行
        this.monthGanWuXing = tianGanWuXingMap.get(getMonthGan()); // 月干五行
        this.dayGanWuXing = tianGanWuXingMap.get(getDayGan());     // 日干五行
        this.hourGanWuXing = tianGanWuXingMap.get(getHourGan());   // 时干五行

        this.yearZhiWuXing = diZhiWuXingMap.get(getYearZhi());   // 年支五行
        this.monthZhiWuXing = diZhiWuXingMap.get(getMonthZhi()); // 月支五行
        this.dayZhiWuXing = diZhiWuXingMap.get(getDayZhi());     // 日支五行
        this.hourZhiWuXing = diZhiWuXingMap.get(getHourZhi());   // 时支五行

        this.yearGanZhiWuXing = getYearGanWuXing() + getYearZhiWuXing();   // 年干支五行
        this.monthGanZhiWuXing = getMonthGanWuXing() + getMonthZhiWuXing(); // 月干支五行
        this.dayGanZhiWuXing = getDayGanWuXing() + getDayZhiWuXing();     // 日干支五行
        this.hourGanZhiWuXing = getHourGanWuXing() + getHourZhiWuXing();   // 时干支五行

        this.baZiWuXing = Arrays.asList(getYearGanZhiWuXing(), getMonthGanZhiWuXing(), getDayGanZhiWuXing(), getHourGanZhiWuXing()); // 八字五行

    }

    /**
     * 计算八字纳音
     */
    private void baZiNaYin() {

        Map<String, String> naYinMap = BaZiMap.NA_YIN; // 纳音（干支为键）

        this.yearGanZhiNaYin = naYinMap.get(getYearGanZhi());   // 年干支纳音
        this.monthGanZhiNaYin = naYinMap.get(getMonthGanZhi()); // 月干支纳音
        this.dayGanZhiNaYin = naYinMap.get(getDayGanZhi());     // 日干支纳音
        this.hourGanZhiNaYin = naYinMap.get(getHourGanZhi());   // 时干支纳音

        this.baZiNaYin = Arrays.asList(getYearGanZhiNaYin(), getMonthGanZhiNaYin(), getDayGanZhiNaYin(), getHourGanZhiNaYin()); // 八字纳音


    }

    /**
     * 计算八字空亡
     */
    private void baZiKongWang() {

        Map<String, String> kongWangMap = BaZiMap.KONG_WANG; // 空亡（干支为键）

        this.yearGanZhiKongWang = kongWangMap.get(getYearGanZhi()); // 年干支空亡
        this.monthGanZhiKongWang = kongWangMap.get(getMonthGanZhi()); // 月干支空亡
        this.dayGanZhiKongWang = kongWangMap.get(getDayGanZhi()); // 日干支空亡
        this.hourGanZhiKongWang = kongWangMap.get(getHourGanZhi()); // 时干支空亡

        this.baZiKongWang = Arrays.asList(getYearGanZhiKongWang(), getMonthGanZhiKongWang(), getDayGanZhiKongWang(), getHourGanZhiKongWang()); // 八字空亡

    }

    /**
     * 计算主星
     */
    private void zhuXing() {

        /*
            ★计算方法：根据日干与年干、月干、时干的生克关系得出

            ========================================================================================

                                               十神生克关系

                1、生我者为印缓（五行阴阳属性相同则为：偏印，五行阴阳属性不同则为：正印）
                2、我生者为子孙（五行阴阳属性相同则为：食神，五行阴阳属性不同则为：伤官）
                3、克我者为官鬼（五行阴阳属性相同则为：七杀，五行阴阳属性不同则为：正官）
                4、我克者为妻财（五行阴阳属性相同则为：偏财，五行阴阳属性不同则为：正财）
                5、同我者为兄弟（五行阴阳属性相同则为：比肩，五行阴阳属性不同则为：劫财）
         */

        Map<String, String> shiShenMap = BaZiMap.SHI_SHEN; // 十神（日干+其他干支为键）

        this.yearGanZhiZhuXing = shiShenMap.get(getDayGan() + getYearGan()); // 年干支主星
        this.monthGanZhiZhuXing = shiShenMap.get(getDayGan() + getMonthGan()); // 月干支主星
        this.dayGanZhiZhuXing = "元" + getSex(); // 日干支主星
        this.hourGanZhiZhuXing = shiShenMap.get(getDayGan() + getHourGan()); // 时干支主星

    }

    /**
     * 计算地支藏干
     */
    private void diZhiCangGan() {

        /*
                 《十二地支藏干口诀》

            子宫壬癸在其中，丑癸辛金己土同；
            寅宫甲木兼丙戊，卯宫甲乙木相逢，
            辰藏乙戊三分癸，已中庚金丙戊丛；
            午宫丙丁火己土，未宫乙己丁共宗；
            申位庚金壬水戊，酉宫庚辛金丰隆；
            戌宫辛金及丁戊，亥藏壬甲戊真踪。

            =========================================================================

            解释：
                01、子：藏干为癸水。
                02、丑：藏干为已土、癸水、辛金。（已土为本气，癸水为中气，辛金为余气）
                03、寅：藏干为甲木、丙火、戊土。（甲木为本气，丙火为中气，戊土为余气）
                04、卯：藏干为乙木。
                05、辰：藏干为戊土、乙木、癸水。（戊土为本气，乙木为中气，癸水为余气）
                06、已：藏干为丙火、庚金、戊土。（丙火为本气，庚金为中气，戊土为余气）
                07、午：藏干为丁火、已土。（丁火为本气，已土为中气）
                08、未：藏干为已土、丁火、乙木。（已土为本气，丁火为中气，乙木为余气）
                09、申：藏干为庚金、壬水、戊土。（庚金为本气，壬水为中气，戊土为余气）
                10、酉：藏干为辛金。
                11、戌：藏干为戊土、辛金、丁火。（戊土为本气，辛金为中气，丁火为余气）
                12、亥：藏干为壬水、甲木。（壬水为本气，甲木为中气）
        */

        Map<String, List<String>> diZhiCangGanMap = BaZiMap.DI_ZHI_CANG_GAN; // 地支藏干（地支为键）

        this.yearZhiCangGan = diZhiCangGanMap.get(getYearZhi());   // 年支藏干
        this.monthZhiCangGan = diZhiCangGanMap.get(getMonthZhi()); // 月支藏干
        this.dayZhiCangGan = diZhiCangGanMap.get(getDayZhi());     // 日支藏干
        this.hourZhiCangGan = diZhiCangGanMap.get(getHourZhi());   // 时支藏干

    }

    /**
     * 计算副星
     */
    private void fuXing() {

        /*
            ★根据日干与年支藏干、月支藏干、日支藏干、时支藏干得出

            ===========================================================================

            十神的生克关系：
                1、生我者为印缓（五行阴阳属性相同则为：枭神(偏印)，五行阴阳属性不同则为：正印）
                2、我生者为子孙（五行阴阳属性相同则为：食神，五行阴阳属性不同则为：伤官）
                3、克我者为官鬼（五行阴阳属性相同则为：七杀(偏官)，五行阴阳属性不同则为：正官）
                4、我克者为妻财（五行阴阳属性相同则为：偏财(才财)，五行阴阳属性不同则为：正财）
                5、同我者为兄弟（五行阴阳属性相同则为：比肩，五行阴阳属性不同则为：劫财）
         */

        Map<String, String> shiShenMap = BaZiMap.SHI_SHEN; // 十神（日干+其他干支为键）

        List<String> yearGanZhiFuXing = new ArrayList<>();
        List<String> monthGanZhiFuXing = new ArrayList<>();
        List<String> dayGanZhiFuXing = new ArrayList<>();
        List<String> hourGanZhiFuXing = new ArrayList<>();
        for (String cangGan : getYearZhiCangGan()) {
            yearGanZhiFuXing.add(shiShenMap.get(getDayGan() + cangGan)); // 年干支副星
        }
        for (String cangGan : getMonthZhiCangGan()) {
            monthGanZhiFuXing.add(shiShenMap.get(getDayGan() + cangGan)); // 月干支副星
        }
        for (String cangGan : getDayZhiCangGan()) {
            dayGanZhiFuXing.add(shiShenMap.get(getDayGan() + cangGan)); // 日干支副星
        }
        for (String cangGan : getHourZhiCangGan()) {
            hourGanZhiFuXing.add(shiShenMap.get(getDayGan() + cangGan)); // 时干支副星
        }

        this.yearGanZhiFuXing = yearGanZhiFuXing; // 年干支副星
        this.monthGanZhiFuXing = monthGanZhiFuXing; // 月干支副星
        this.dayGanZhiFuXing = dayGanZhiFuXing; // 日干支副星
        this.hourGanZhiFuXing = hourGanZhiFuXing; // 时干支副星

    }

    /**
     * 计算自坐
     */
    private void ziZuo() {

        Map<String, String> shiErZhangShengMap = BaZiMap.SHI_ER_ZHANG_SHENG; // 十二长生（日干+地支为键）

        this.yearGanZhiZiZuo = shiErZhangShengMap.get(getYearGan() + getYearZhi()); // 年干支自坐
        this.monthGanZhiZiZuo = shiErZhangShengMap.get(getMonthGan() + getMonthZhi()); // 月干支自坐
        this.dayGanZhiZiZuo = shiErZhangShengMap.get(getDayGan() + getDayZhi()); // 日干支自坐
        this.hourGanZhiZiZuo = shiErZhangShengMap.get(getHourGan() + getHourZhi()); // 时干支自坐

    }

    /**
     * 计算星运
     */
    private void xingYun() {

        Map<String, String> shiErZhangShengMap = BaZiMap.SHI_ER_ZHANG_SHENG; // 十二长生（日干+地支为键）

        this.yearGanZhiXingYun = shiErZhangShengMap.get(getDayGan() + getYearZhi()); // 年干支星运
        this.monthGanZhiXingYun = shiErZhangShengMap.get(getDayGan() + getMonthZhi()); // 月干支星运
        this.dayGanZhiXingYun = shiErZhangShengMap.get(getDayGan() + getDayZhi()); // 日干支星运
        this.hourGanZhiXingYun = shiErZhangShengMap.get(getDayGan() + getHourZhi()); // 时干支星运

    }

    /**
     * 计算常用神煞
     */
    private void shenSha() {

        BaZiShenShaUtil baZiShenShaUtil = new BaZiShenShaUtil(getSex(), getSeason(), getYearGanZhiNaYin(), getYearGanZhi(), getMonthGanZhi(), getDayGanZhi(), getHourGanZhi(), CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY);

        this.yearGanZhiShenSha = baZiShenShaUtil.getYearGanZhiShenSha();   // 年干支神煞
        this.monthGanZhiShenSha = baZiShenShaUtil.getMonthGanZhiShenSha(); // 月干支神煞
        this.dayGanZhiShenSha = baZiShenShaUtil.getDayGanZhiShenSha();     // 日干支神煞
        this.hourGanZhiShenSha = baZiShenShaUtil.getHourGanZhiShenSha();   // 时干支神煞
        this.xiaoErGuanSha = baZiShenShaUtil.getXiaoErGuanSha(); // 小儿关煞

    }

    /**
     * 计算天干留意
     */
    private void tianGanLiuYi() {

        this.tianGanLiuYi = BaZiUtil.tanGanLiuYi(getYearGan(), getMonthGan(), getDayGan(), getHourGan(), CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY);

    }

    /**
     * 计算地支留意
     */
    private void diZhiLiuYi() {

        this.diZhiLiuYi = BaZiUtil.diZhiLiuYi(getYearZhi(), getMonthZhi(), getDayZhi(), getHourZhi(), CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY, CommonUtil.EMPTY);

    }

    /**
     * 计算上一节及日期、下一节及日期、出生节气
     */
    private void jieQi() {

        Solar solar = getSolar(); // 公历日期
        Lunar lunar = getLunar(); // 农历日期

        // 1、计算上一节、上一节日期
        JieQi prevJie = lunar.getPrevJie(true);
        this.prevJie = prevJie.toString(); // 上一节
        String prevSolar = prevJie.getSolar().toYmdHms();
        this.prevJieDateStr = prevSolar; // 上一节日期

        // 2、计算下一节、下一节日期
        JieQi nextJie = lunar.getNextJie(true);
        this.nextJie = nextJie.toString(); // 下一节
        String nextSolar = nextJie.getSolar().toYmdHms();
        this.nextJieDateStr = nextSolar; // 下一节日期

        // 3、计算上一节与出生日期的时间间隔
        Map<String, Long> prevMap = DateUtil.dateInterval(prevSolar, solar.toYmdHms());

        // 4、计算出生日期与下一节的时间间隔
        Map<String, Long> nextMap = DateUtil.dateInterval(solar.toYmdHms(), nextSolar);

        // 5、设置数据
        this.prevJieDayNumber = Math.toIntExact(prevMap.get("days")); // 所选日期距上一节气天数
        this.nextJieDayNumber = Math.toIntExact(nextMap.get("days")); // 所选日期距下一节气天数
        long prevDay = prevMap.get("days") > 0 ? prevMap.get("days") : -prevMap.get("days"); // 天
        long prevHours = prevMap.get("hours") > 0 ? prevMap.get("hours") : -prevMap.get("hours"); // 小时
        long prevMinutes = prevMap.get("minutes") > 0 ? prevMap.get("minutes") : -prevMap.get("minutes"); // 分
        long prevSeconds = prevMap.get("seconds") > 0 ? prevMap.get("seconds") : -prevMap.get("seconds"); // 秒
        long nextDay = nextMap.get("days") > 0 ? nextMap.get("days") : -nextMap.get("days"); // 天
        long nextHours = nextMap.get("hours") > 0 ? nextMap.get("hours") : -nextMap.get("hours"); // 小时
        long nextMinutes = nextMap.get("minutes") > 0 ? nextMap.get("minutes") : -nextMap.get("minutes"); // 分
        long nextSeconds = nextMap.get("seconds") > 0 ? nextMap.get("seconds") : -nextMap.get("seconds"); // 秒
        String prevStr = "出生于" + prevJie + "后" + prevDay + "天" + prevHours + "小时" + prevMinutes + "分" + prevSeconds + "秒";
        String nextStr = nextJie + "前" + nextDay + "天" + nextHours + "小时" + nextMinutes + "分" + nextSeconds + "秒";
        this.birthSolarTerms = prevStr + "、" + nextStr;

    }

    /**
     * 计算人元司令分野
     *
     * @param setting 八字-设置
     */
    private void renYuan(BaZiSetting setting) {

        String renYuan = "";

        int prevJieDayNumber = getPrevJieDayNumber() > 29 ? 1 : getPrevJieDayNumber(); // TODO：已知BUG，暂未修复

        // 1、判断计算方式
        if (setting.getRenYuanType() == 0) {
            // 1.1、子平真诠法诀
            Map<String, List<String>> ziRenYuan = BaZiMap.ZI_REN_YUAN;
            renYuan = ziRenYuan.get(getMonthZhi()).get(prevJieDayNumber);
        } else if (setting.getRenYuanType() == 1) {
            // 1.2、渊海子平法诀
            Map<String, List<String>> yuanRenYuan = BaZiMap.YUAN_REN_YUAN;
            renYuan = yuanRenYuan.get(getMonthZhi()).get(prevJieDayNumber);
        } else if (setting.getRenYuanType() == 2) {
            // 1.3、星平会海法诀
            Map<String, List<String>> xingRenYuan = BaZiMap.XING_REN_YUAN;
            renYuan = xingRenYuan.get(getMonthZhi()).get(prevJieDayNumber);
        } else if (setting.getRenYuanType() == 3) {
            // 1.4、三命通会法诀
            Map<String, List<String>> xingRenYuan = BaZiMap.SAN_REN_YUAN;
            renYuan = xingRenYuan.get(getMonthZhi()).get(prevJieDayNumber);
        } else if (setting.getRenYuanType() == 4) {
            // 1.5、神峰通考法诀
            Map<String, List<String>> xingRenYuan = BaZiMap.SHEN_REN_YUAN;
            renYuan = xingRenYuan.get(getMonthZhi()).get(prevJieDayNumber);
        } else if (setting.getRenYuanType() == 5) {
            // 1.6、万育吾之法诀
            Map<String, List<String>> xingRenYuan = BaZiMap.WAN_REN_YUAN;
            renYuan = xingRenYuan.get(getMonthZhi()).get(prevJieDayNumber);
        }

        // 2、处理数据
        Map<String, String> ganWuXing = BaZiMap.TIAN_GAN_WU_XING; // 天干五行
        if (!"".equals(renYuan)) {
            String wuXing = ganWuXing.get(renYuan); // 获取人元五行
            this.renYuan = renYuan + wuXing + "值令";
        }

    }

    /**
     * 计算星宿、星宿吉凶、星宿吉凶歌诀
     */
    private void xingXiu() {

        this.xingXiu = getLunar().getXiu() + "宿" + getLunar().getGong() + "方" + getLunar().getShou(); // 星宿
        this.xiuJiXiong = getLunar().getXiuLuck(); // 星宿吉凶
        this.xiuJiXiongGeJue = getLunar().getXiuSong(); // 星宿吉凶歌诀

    }

    /**
     * 计算胎元、胎息、命宫、身宫
     */
    private void taiGong() {

        this.taiYuan = getEightChar().getTaiYuan() + "（" + getEightChar().getTaiYuanNaYin() + "）"; // 胎元
        this.taiXi = getEightChar().getTaiXi() + "（" + getEightChar().getTaiXiNaYin() + "）"; // 胎息
        this.mingGong = getEightChar().getMingGong() + "（" + getEightChar().getMingGongNaYin() + "）"; // 命宫
        this.shenGong = getEightChar().getShenGong() + "（" + getEightChar().getShenGongNaYin() + "）"; // 身宫

    }

    /**
     * 计算命、命卦、命卦信息
     */
    private void mingGua() {

        /*
            一、男命：
                1、年份中的每一个数字（例如：2000年→ 2+0+0+0=2）。
                2、相加得到数字A（例如：2）。
                3、用得到数字A的每一个数字相加再得到数字B（例如：2）。
                4、若数字B小于10，则使用(11-数字B);若数字B大于10，则使用(数字B-11)。

            二、女命：
                1、年份中的每一个数字（例如：2000年→ 2+0+0+0=2）。
                2、相加得到一个数字A（例如：2）。
                3、用数字A+4得到数字B（例如：2+4=6）。
                4、若数字B大于9，再用数字B-9；如没有余数或除尽则视为9。
         */

        Map<Integer, String> mingGuaMap = BaZiMap.MING_GUA; // 命卦（命卦宫位为键）
        Map<String, List<String>> mingGuaFenXiMap = BaZiMap.MING_GUA_FEN_XI; // 命卦分析（命卦为键）

        // 1、计算命
        this.ming = getYearGanZhiNaYin() + "命";

        // 2、计算年份合数
        int yearCount = 0; // 年份合数
        String yearStr = String.valueOf(getSolar().getYear());
        for (int i = 0; i < yearStr.length(); i++) {
            yearCount += Integer.parseInt(yearStr.split("")[i]);
        }

        // 3、男命
        // 获取命卦基本分析
        if ("男".equals(getSex())) {
            // 3.1、若年份合数大于10，则继续使用年份合数相加，直至小于10为止
            while (true) {
                if (yearCount > 10) {
                    String yearCountStr = String.valueOf(yearCount);
                    yearCount = 0;
                    for (int i = 0; i < yearCountStr.length(); i++) {
                        yearCount += Integer.parseInt(yearCountStr.split("")[i]);
                    }
                } else break;
            }
            // 3.2、使用（11-数字之和）得出男命命卦宫位
            yearCount = 11 - yearCount == 5 ? 2 : 11 - yearCount; // 男命命卦宫位为5时，寄坤宫
            this.mingGua = mingGuaMap.get(yearCount); // 获取命卦
        } else {
            // 4、女命
            int womManMingGuaGong = yearCount + 4; // 数字B
            // 4.1、若数字B大于9，再用数字B-9；如没有余数或除尽则视为9
            while (true) {
                if (womManMingGuaGong > 9) {
                    womManMingGuaGong -= 9;
                } else break;
            }
            // 4.2、得出女命命卦宫位，并获取命卦
            womManMingGuaGong = womManMingGuaGong == 5 ? 8 : womManMingGuaGong; // 女命命卦宫位为5时，寄艮宫
            this.mingGua = mingGuaMap.get(womManMingGuaGong); // 获取命卦
        }

        this.mingGuaJiBen = mingGuaFenXiMap.get(getMingGua()).get(0); // 获取命卦基本分析
        this.mingGuaFengShui = mingGuaFenXiMap.get(getMingGua()).get(1); // 获取命卦基本分析

    }

    /**
     * 计算五行缺失
     */
    private void wuXingQueShi() {

        Map<String, String> tianGanWuXingMap = BaZiMap.TIAN_GAN_WU_XING; // 天干五行（天干为键）
        Map<String, String> diZhiWuXingMap = BaZiMap.DI_ZHI_WU_XING; // 地支五行（地支为键）

        // 1、添加八字五行数据
        Set<String> set = new HashSet<>(Arrays.asList(
                tianGanWuXingMap.get(getYearGan()), tianGanWuXingMap.get(getMonthGan()), tianGanWuXingMap.get(getDayGan()), tianGanWuXingMap.get(getHourGan()),
                diZhiWuXingMap.get(getYearZhi()), diZhiWuXingMap.get(getMonthZhi()), diZhiWuXingMap.get(getDayZhi()), diZhiWuXingMap.get(getHourZhi())
        ));
        List<String> list = new ArrayList<>(set);

        // 2、判断五行是否无缺失
        List<String> wuXingQueShi = new ArrayList<>();
        if (list.size() == 5) {
            wuXingQueShi.add("五行齐全");
            this.wuXingQueShi = wuXingQueShi;
            return;
        }

        // 3、获取并设置缺失的五行
        wuXingQueShi = CommonUtil.getListUnlike(list, Arrays.asList("木", "火", "土", "金", "水"));
        this.wuXingQueShi = wuXingQueShi;

    }

    /**
     * 计算五行数量
     */
    private void wuXingCount() {

        Map<String, String> ganWuXingMap = BaZiMap.TIAN_GAN_WU_XING; // 天干五行（天干为键）

        // 1、计算天干五行数量
        addGanWuXingCount(getYearGanWuXing()); // 年干五行
        addGanWuXingCount(getMonthGanWuXing()); // 月干五行
        addGanWuXingCount(getDayGanWuXing()); // 日干五行
        addGanWuXingCount(getHourGanWuXing()); // 时干五行

        // 2、计算地支五行数量
        addZhiWuXingCount(getYearZhiWuXing()); // 年支五行
        addZhiWuXingCount(getMonthZhiWuXing()); // 月支五行
        addZhiWuXingCount(getDayZhiWuXing()); // 日支五行
        addZhiWuXingCount(getHourZhiWuXing()); // 时支五行

        // 3、计算藏干五行数量
        for (int i = 0; i < getYearZhiCangGan().size(); i++) {
            addCangGanWuXingCount(ganWuXingMap.get(getYearZhiCangGan().get(i))); // 年支藏干五行
        }
        for (int i = 0; i < getMonthZhiCangGan().size(); i++) {
            addCangGanWuXingCount(ganWuXingMap.get(getMonthZhiCangGan().get(i))); // 月支藏干五行
        }
        for (int i = 0; i < getDayZhiCangGan().size(); i++) {
            addCangGanWuXingCount(ganWuXingMap.get(getDayZhiCangGan().get(i))); // 日支藏干五行
        }
        for (int i = 0; i < getHourZhiCangGan().size(); i++) {
            addCangGanWuXingCount(ganWuXingMap.get(getHourZhiCangGan().get(i))); // 时支藏干五行
        }

        // 4、设置五行数量（不包含藏干）
        this.wuXingCount = Arrays.asList(getGanMuCount() + getZhiMuCount() + "木", getGanHuoCount() + getZhiHuoCount() + "火", getGanTuCount() + getZhiTuCount() + "土", getGanJinCount() + getZhiJinCount() + "金", getGanShuiCount() + getZhiShuiCount() + "水");

    }

    /**
     * 计算五行旺衰
     */
    private void wuXingWangShuai() {

        this.wuXingWangShuai = BaZiMap.WU_XING_WANG_SHUAI.get(getMonthZhi());

    }

    /**
     * 计算身体强弱
     */
    private void shenTiQiangRuo() {

        /*
            1、根据[日元]和[月令]之间的五行生克关系计算。

                解释：日元代表自己，月令代表父母或兄弟。
                口诀：同我、生我为强；克我、我生、我克为弱。
            ----------------------------------------------------

            2、根据[日元]和[其他干支]之间的算分进行计算。

            ----------------------------------------------------
            TODO：此处采用第2种方法：根据[日元]和[其他干支]之间的算分进行计算，暂不考虑其他各种因素。
         */

        Map<String, String> shengKeMap = BaZiMap.GAN_ZHI_WU_XING_SHENG_KE; // 天干和干支的五行生克关系

        // 1、判断身体强弱（根据[日元]和[月令]之间的五行生克关系计算）
//        String bodyIntensity = shengKe.get(getDayGan() + getMonthZhi());
//        System.out.println("身体强弱：" + bodyIntensity);

        // 2、判断身体强弱（根据算分计算）
        int jiaFen = 0; // 加分
        // 2.1、通过日元（日干）与年干的五行生克关系算出加分
        jiaFen += jiaFen(shengKeMap, getDayGan(), getYearGan(), 8); // 年干统一为8分
        // 2.2、通过日元（日干）与年支的五行生克关系算出加分
        jiaFen += jiaFen(shengKeMap, getDayGan(), getYearZhi(), 4); // 年支统一为4分
        // 2.3、通过日元（日干）与月干的五行生克关系算出加分
        jiaFen += jiaFen(shengKeMap, getDayGan(), getMonthGan(), 12); // 月干统一为12分
        // 2.4、通过日元（日干）与月支的五行生克关系算出加分
        jiaFen += jiaFen(shengKeMap, getDayGan(), getMonthZhi(), 40); // 月支统一为40分
        // 2.5、通过日元（日干）与日支的五行生克关系算出加分
        jiaFen += jiaFen(shengKeMap, getDayGan(), getDayZhi(), 12); // 日支统一为12分
        // 2.6、通过日元（日干）与时干的五行生克关系算出加分
        jiaFen += jiaFen(shengKeMap, getDayGan(), getHourGan(), 12); // 时干统一为12分
        // 2.7、通过日元（日干）与时支的五行生克关系算出加分
        jiaFen += jiaFen(shengKeMap, getDayGan(), getHourZhi(), 12); // 时支统一为12分

        // 3.1、若加分等于50，身体强弱为：平衡（中庸之道）
        if (jiaFen == 50) {
            this.shenTiQiangRuo = "平衡"; // 平衡（中庸之道）
        }
        // 3.2、若加分>=40且<=60，身体强弱为：接近平衡（中庸之道）
        if (jiaFen >= 40 && jiaFen <= 60) {
            this.shenTiQiangRuo = "接近平衡"; // 接近平衡（中庸之道）
        }
        // 3.3、若加分小于40，身体强弱为：身弱
        if (jiaFen < 40) {
            this.shenTiQiangRuo = "身弱"; // 身弱
            return;
        }
        // 3.4、若加分大于60，身体强弱为：身强
        if (jiaFen > 60) {
            this.shenTiQiangRuo = "身强"; // 身强
        }

    }

    /**
     * 计算喜用神
     */
    private void xiYongShen() {

        /*
            身强的喜用神：克我、我生、我克的干支。
            身弱的喜用神：同我、生我的干支。
        */

        Set<String> set = new HashSet<>();
        Map<String, String> tianGanWuXingMap = BaZiMap.TIAN_GAN_WU_XING; // 天干五行（天干为键）
        Map<String, String> diZhiWuXingMap = BaZiMap.DI_ZHI_WU_XING; // 地支五行（地支为键）
        Map<String, String> shengKeMap = BaZiMap.GAN_ZHI_WU_XING_SHENG_KE;// 天干和干支的五行生克关系（天干+干支为键）

        // 1、若身强，则寻找：克我、我生、我克的干支
        if ("身强".equals(getShenTiQiangRuo())) {
            // 1.1、判断日元（日干）与年干的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getYearGan()))) set.add(tianGanWuXingMap.get(getYearGan())); // 添加年干五行
            // 1.2、判断日元（日干）与年支的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getYearZhi()))) set.add(diZhiWuXingMap.get(getYearZhi())); // 添加年支五行
            // 1.3、判断日元（日干）与月干的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getMonthGan()))) set.add(tianGanWuXingMap.get(getMonthGan())); // 添加月干五行
            // 1.4、判断日元（日干）与月支的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getMonthZhi()))) set.add(diZhiWuXingMap.get(getMonthZhi())); // 添加月支五行
            // 1.5、判断日元（日干）与日干的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getDayGan()))) set.add(tianGanWuXingMap.get(getDayGan())); // 添加日干五行
            // 1.6、判断日元（日干）与日支的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getDayZhi()))) set.add(diZhiWuXingMap.get(getDayZhi())); // 添加日支五行
            // 1.7、判断日元（日干）与时干的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getHourGan()))) set.add(tianGanWuXingMap.get(getHourGan())); // 添加时干五行
            // 1.8、判断日元（日干）与时支的五行生克关系
            if ("身弱".equals(shengKeMap.get(getDayGan() + getHourZhi()))) set.add(diZhiWuXingMap.get(getHourZhi())); // 添加时支五行
            this.xiYongShen = new ArrayList<>(set);
            return;
        }

        // 2、若身弱，则寻找：同我、生我的干支
        if ("身弱".equals(getShenTiQiangRuo())) {
            // 2.1、判断日元（日干）与年干的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getYearGan()))) set.add(tianGanWuXingMap.get(getYearGan())); // 添加年干的五行
            // 2.2、判断日元（日干）与年支的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getYearZhi()))) set.add(diZhiWuXingMap.get(getYearZhi())); // 添加年支的五行
            // 2.3、判断日元（日干）与月干的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getMonthGan()))) set.add(tianGanWuXingMap.get(getMonthGan())); // 添加月干的五行
            // 2.4、判断日元（日干）与月支的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getMonthZhi()))) set.add(diZhiWuXingMap.get(getMonthZhi())); // 添加月支的五行
            // 2.5、判断日元（日干）与日干的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getDayGan()))) set.add(tianGanWuXingMap.get(getDayGan())); // 添加日干五行
            // 2.6、判断日元（日干）与日支的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getDayZhi()))) set.add(diZhiWuXingMap.get(getDayZhi())); // 添加日支的五行
            // 2.7、判断日元（日干）与时干的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getHourGan()))) set.add(tianGanWuXingMap.get(getHourGan())); // 添加时干的五行
            // 2.8、判断日元（日干）与时支的五行生克关系
            if ("身强".equals(shengKeMap.get(getDayGan() + getHourZhi()))) set.add(diZhiWuXingMap.get(getHourZhi())); // 添加时支的五行
            this.xiYongShen = new ArrayList<>(set);
            return;
        }

        // 3、若身体为平衡或接近平衡，则使用具体法获取喜用神
        String season = getLunar().getSeason().substring(1, 2); // 获取季节简称（如：春）
        int lunarMonth = getLunar().getMonth(); // 农历月
        if (lunarMonth == 3 || lunarMonth == 6 || lunarMonth == 9 || lunarMonth == 12) season = "四季末";
        this.xiYongShen = BaZiMap.XI_YONG_SHEN.get(getDayGan() + season);

    }

    /**
     * 计算喜用神方位
     */
    private void xiYongShenFangWei() {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < getXiYongShen().size(); i++) {
            String xiYongShen = getXiYongShen().get(i); // 每一个喜用神的方位
            List<String> xiYongFangWei = BaZiMap.WU_XING_FANG_WEI.get(xiYongShen);
            list.addAll(xiYongFangWei); // 保存所有喜用神的方位
        }

        this.xiYongShenFangWei = list;

    }

    /**
     * 计算骨重
     */
    private void guZhong() {

        int yearGuZhong = BaZiMap.YEAR_GU_ZHONG.get(getYearGanZhi()); // 出生年骨重
        int monthGuZhong = BaZiMap.MONTH_GU_ZHONG.get(getMonthZhi()); // 出生月骨重
        int dayGuZhong = BaZiMap.DAY_GU_ZHONG.get(getLunar().getDayInChinese()); // 出生日骨重
        int hourGuZhong = BaZiMap.HOUR_GU_ZHONG.get(getHourZhi()); // 出生时辰骨重
        int guZhong = yearGuZhong + monthGuZhong + dayGuZhong + hourGuZhong;
        guZhong = Math.max(guZhong, 21); // 最轻骨重

        if ("男".equals(getSex())) {
            // 2.1、男命
            guZhong = Math.min(guZhong, 72); // 男命的最重骨重
            this.guZhong = guZhongCharacters(guZhong); // 骨重转为文字
            this.guZhongPiZhu = BaZiMap.MAN_GU_ZHONG_YUAN_WEN.get(guZhong); // 骨重批注（男命）
        } else {
            // 2.2、女命
            guZhong = Math.min(guZhong, 71); // 女命的最重骨重
            this.guZhong = guZhongCharacters(guZhong); // 骨重转为文字
            this.guZhongPiZhu = BaZiMap.WOMAN_GU_ZHONG_YUAN_WEN.get(guZhong); // 骨重批注（男命）
        }

    }

    /**
     * 计算日柱论命
     */
    private void dayZhuLunMing() {

        this.dayZhuLunMing = BaZiMap.DAY_ZHU_LUN_MING.get(getDayGanZhi());

    }

    /**
     * 计算姻缘分析
     */
    private void yinYuanFenXi() {

        String dayGanZhiNaYin = getDayGanZhiNaYin().substring(2, 3); // 日干支纳音（如：木）
        int lunarMonth = getLunar().getMonth(); // 农历月
        if (lunarMonth < 0) lunarMonth = -lunarMonth;

        this.yinYuanFenXi = BaZiMap.YIN_YUAN_FEN_XI.get(dayGanZhiNaYin + lunarMonth);

    }

    /**
     * 计算五行分析
     */
    private void wuXingFenXi() {

        this.wuXingFenXi = "命主生于" + getYearGanZhi() + "年为" + getYearGanZhiNaYin() + "命，" + BaZiMap.WU_XING_FEN_XI.get(getYearGanZhiNaYin().substring(2, 3));

    }

    /**
     * 计算大运
     */
    private DaYun[] daYun() {

        List<List<String>> daYun = new ArrayList<>(); // 大运

        // 获取指定轮数的大运
        DaYun[] daYunSource = getYun().getDaYun(12);
        for (DaYun dy : daYunSource) {
            // 添加大运
            daYun.add(new ArrayList<>(Arrays.asList(String.valueOf(dy.getStartYear()), String.valueOf(dy.getStartAge()), dy.getGanZhi())));
        }

        // 计算小运持续的年数（根据公历年计算）
        this.xiaoYunYearSum = Integer.parseInt(daYun.get(1).get(1)) - Integer.parseInt(daYun.get(0).get(1));

        this.daYun = daYun; // 大运

        return daYunSource;

    }

    /**
     * 计算流年
     *
     * @param daYun 大运
     */
    private void liuNian(DaYun[] daYun) {

        List<List<String>> liuNianList = new ArrayList<>(); // 流年

        LiuNian[] liuNian;

        // 遍历大运
        for (DaYun key : daYun) {
            // 获取10轮流年
            liuNian = key.getLiuNian(10);
            for (LiuNian ln : liuNian) {
                // 添加流年
                liuNianList.add(new ArrayList<>(Arrays.asList(String.valueOf(ln.getYear()), String.valueOf(ln.getAge()), ln.getGanZhi())));
            }
        }

        this.liuNian = liuNianList; // 流年

    }

    /**
     * 计算财运年份、年龄、干支、合化
     */
    private void caiYun() {

        List<List<String>> zhengCaiList = new ArrayList<>(); // 保存正财信息
        List<List<String>> pianCaiList = new ArrayList<>(); // 保存偏财信息

        caiTaoHua(BaZiMap.RI_ZHU_ZHENG_CAI, BaZiMap.RI_ZHU_PIAN_CAI, zhengCaiList, pianCaiList);

        this.zhengCaiYun = zhengCaiList; // 正财运年份、年龄、干支
        this.pianCaiYun = pianCaiList; // 偏财运年份、年龄、干支

    }

    /**
     * 计算桃花年份、年龄、干支、合化
     */
    private void taoHua() {

        // 1、男命
        if ("男".equals(getSex())) {
            this.zhengTaoHua = getZhengCaiYun(); // 正桃花年份、年龄、干支
            this.pianTaoHua = getPianCaiYun(); // 偏桃花年份、年龄、干支
            return;
        }

        List<List<String>> zhengTaoHuaList = new ArrayList<>(); // 保存正桃花信息
        List<List<String>> pianTaoHuaList = new ArrayList<>(); // 保存偏桃花信息

        // 2、女命
        caiTaoHua(BaZiMap.NV_RI_ZHU_ZHENG_TAO_HUA, BaZiMap.NV_RI_ZHU_PIAN_TAO_HUA, zhengTaoHuaList, pianTaoHuaList);
        this.zhengTaoHua = zhengTaoHuaList; // 正桃花年份、年龄、干支
        this.pianTaoHua = pianTaoHuaList; // 偏桃花年份、年龄、干支

    }

    /**
     * 计算起运、交运
     */
    private void qiYunJiaoYun() {

        // 1、计算起运
        String qiYun = "出生";
        if (getYun().getStartYear() != 0) qiYun += getYun().getStartYear() + "年";
        if (getYun().getStartMonth() != 0) qiYun += getYun().getStartMonth() + "个月";
        if (getYun().getStartDay() != 0) qiYun += getYun().getStartDay() + "天";
        if (getYun().getStartHour() != 0) qiYun += getYun().getStartHour() + "小时";
        qiYun += "后开始起运";

        this.qiYun = qiYun; // 起运
        this.qiYunDateStr = getYun().getStartSolar().toYmdHms(); // 起运日期

        // 2、计算交运
        // 2.1、通过起运年份的流年干计算交运干
        String jiaoYunGan = BaZiMap.JIAO_YUN_GAN.get(getLiuNian().get(getXiaoYunYearSum()).get(2).substring(0, 1));
        // 2.2、计算交运农历日期
        Lunar lunar = new Lunar(DateUtil.updateDate(getSolarDate(), getYun().getStartYear(), getYun().getStartMonth(), getYun().getStartDay()));
        // 2.3、计算上一节、交运农历日期距上一节的天数
        String prevJie = ""; // 上一节
        int prevJieDay = 0; // 交运农历日期距上一节的天数
        for (int i = 1; i < 60; i++) {
            prevJie = lunar.next(-i).getJie(); // 向前第i天的节，若没有则返回 ""
            if ("".equals(prevJie)) {
                prevJieDay++;
            } else break;
        }

        // 3、设置数据
        this.jiaoYun = "逢" + jiaoYunGan + "年，" + prevJie + "后" + prevJieDay + "天交运";

    }

//------------------------------------------------------------------------------------------------------------------------------------

    /**
     * 判断天干五行并添加数量
     *
     * @param ganWuXing 天干五行
     */
    protected void addGanWuXingCount(String ganWuXing) {

        if ("木".equals(ganWuXing)) {
            this.ganMuCount++;
        } else if ("火".equals(ganWuXing)) {
            this.ganHuoCount++;
        } else if ("土".equals(ganWuXing)) {
            this.ganTuCount++;
        } else if ("金".equals(ganWuXing)) {
            this.ganJinCount++;
        } else if ("水".equals(ganWuXing)) {
            this.ganShuiCount++;
        }

    }

    /**
     * 判断地支五行并添加数量
     *
     * @param zhiWuXing 天干五行
     */
    protected void addZhiWuXingCount(String zhiWuXing) {

        if ("木".equals(zhiWuXing)) {
            this.zhiMuCount++;
        } else if ("火".equals(zhiWuXing)) {
            this.zhiHuoCount++;
        } else if ("土".equals(zhiWuXing)) {
            this.zhiTuCount++;
        } else if ("金".equals(zhiWuXing)) {
            this.zhiJinCount++;
        } else if ("水".equals(zhiWuXing)) {
            this.zhiShuiCount++;
        }

    }

    /**
     * 判断藏干五行并添加数量
     *
     * @param cangGanWuXing 藏干五行
     */
    protected void addCangGanWuXingCount(String cangGanWuXing) {

        if ("木".equals(cangGanWuXing)) {
            this.cangGanMuCount++;
        } else if ("火".equals(cangGanWuXing)) {
            this.cangGanHuoCount++;
        } else if ("土".equals(cangGanWuXing)) {
            this.cangGanTuCount++;
        } else if ("金".equals(cangGanWuXing)) {
            this.cangGanJinCount++;
        } else if ("水".equals(cangGanWuXing)) {
            this.cangGanShuiCount++;
        }

    }

    /**
     * 返回加分
     *
     * @param shengKe   天干和干支五行的生克关系
     * @param ganOrZhi1 天干或地支
     * @param ganOrZhi2 天干或地支
     * @param jia       加分
     * @return 加分
     */
    protected int jiaFen(Map<String, String> shengKe, String ganOrZhi1, String ganOrZhi2, int jia) {

        int fen = 0;

        String qiangOrRuo = shengKe.get(ganOrZhi1 + ganOrZhi2); // '身强'或'身弱'
        // 若身强则加分
        if ("身强".equals(qiangOrRuo)) {
            fen += jia;
        }

        return fen;

    }

    /**
     * 骨重转为文字
     *
     * @param guZhong 骨重
     * @return 骨重文字
     */
    protected String guZhongCharacters(Integer guZhong) {

        long liang = Long.parseLong(guZhong.toString().substring(0, 1)); // 两重
        long qian = Long.parseLong(guZhong.toString().substring(1, 2)); // 钱重

        String newGuZhong = CommonUtil.shuToHan(liang) + "两";
        if (0 != qian) {
            newGuZhong += CommonUtil.shuToHan(qian) + "钱";
        }

        return newGuZhong;

    }

    /**
     * 计算财运或桃花运相关信息
     *
     * @param zhengCaiOrZhengTaoHuaMap  正财集合或正桃花集合
     * @param pianCaiOrZhengTaoHuaMap   偏财集合或偏桃花集合
     * @param zhengCaiOrZhengTaoHuaList 保存正财或正桃花信息
     * @param pianCaiOrZhengTaoHuaList  保存偏财或偏桃花信息
     */
    protected void caiTaoHua(Map<String, List<String>> zhengCaiOrZhengTaoHuaMap, Map<String, List<String>> pianCaiOrZhengTaoHuaMap, List<List<String>> zhengCaiOrZhengTaoHuaList, List<List<String>> pianCaiOrZhengTaoHuaList) {

        for (List<String> liuNian : getLiuNian()) {
            String liuNianGan = liuNian.get(2).substring(0, 1); // 流年天干
            String liuNianZhi = liuNian.get(2).substring(1, 2); // 流年地支
            List<String> zhengCaiOrZhengTaoHuaGanZhi = zhengCaiOrZhengTaoHuaMap.get(getDayGan()); // 正财干支或正桃花干支
            List<String> pianCaiOrPianTaoHuaGanZhi = pianCaiOrZhengTaoHuaMap.get(getDayGan()); // 正桃花干支或偏桃花干支
            // 遍历日干正桃花干支（偏桃花数量与正桃花相同）
            for (int i = 0; i < zhengCaiOrZhengTaoHuaGanZhi.size(); i++) {
                // 判断流年干支是否为正财或正桃花
                if (liuNianGan.equals(zhengCaiOrZhengTaoHuaGanZhi.get(i)) || liuNianZhi.equals(zhengCaiOrZhengTaoHuaGanZhi.get(i))) {
//                    zhengCaiOrZhengTaoHuaList.add(liuNian);
                    // 判断流年干是否为正财
                    if (liuNianGan.equals(zhengCaiOrZhengTaoHuaGanZhi.get(i))) zhengCaiOrZhengTaoHuaList.add(getSiZhuGanZhiHeHua(liuNian, liuNianGan, true));
                    // 判断流年支是否为正财
                    if (liuNianZhi.equals(zhengCaiOrZhengTaoHuaGanZhi.get(i))) zhengCaiOrZhengTaoHuaList.add(getSiZhuGanZhiHeHua(liuNian, liuNianZhi, false));
                    break;
                }
                // 判断流年干支是否为偏财或偏桃花
                if (liuNianGan.equals(pianCaiOrPianTaoHuaGanZhi.get(i)) || liuNianZhi.equals(pianCaiOrPianTaoHuaGanZhi.get(i))) {
//                    pianCaiOrZhengTaoHuaList.add(liuNian);
                    // 判断流年干是否为偏财
                    if (liuNianGan.equals(pianCaiOrPianTaoHuaGanZhi.get(i))) pianCaiOrZhengTaoHuaList.add(getSiZhuGanZhiHeHua(liuNian, liuNianGan, true));
                    // 判断流年支是否为偏财
                    if (liuNianZhi.equals(pianCaiOrPianTaoHuaGanZhi.get(i))) pianCaiOrZhengTaoHuaList.add(getSiZhuGanZhiHeHua(liuNian, liuNianZhi, false));
                }
            }
        }

    }

    /**
     * 判断并添加四柱干支与流年干支是否构成合化
     *
     * @param list            数据集合
     * @param liuNianGanOrZhi 流年天干或流年地支
     * @param heHuaType       合化计算方式（true:计算天干合化。false:计算地支合化）
     */
    protected List<String> getSiZhuGanZhiHeHua(List<String> list, String liuNianGanOrZhi, boolean heHuaType) {

        Map<String, String> tianGanXiangHeMap = BaZiMap.TIAN_GAN_XIANG_HE; // 天干相合（天干+天干为键）
        Map<String, String> diZhiLiuHeMap = BaZiMap.DI_ZHI_LIU_HE; // 地支六合（地支+地支为键）

        String heHua = "";
        if (heHuaType) {
            // 判断四柱天干与流年天干是否构成合化
            if (null != tianGanXiangHeMap.get(getYearGan() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年干[" + liuNianGanOrZhi + "]被年干[" + getYearGan() + "]合化(" + tianGanXiangHeMap.get(getYearGan() + liuNianGanOrZhi).substring(2) + ")";
            if (null != tianGanXiangHeMap.get(getMonthGan() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年干[" + liuNianGanOrZhi + "]被月干[" + getMonthGan() + "]合化(" + tianGanXiangHeMap.get(getMonthGan() + liuNianGanOrZhi).substring(2) + ")";
            if (null != tianGanXiangHeMap.get(getDayGan() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年干[" + liuNianGanOrZhi + "]被日干[" + getDayGan() + "]合化(" + tianGanXiangHeMap.get(getDayGan() + liuNianGanOrZhi).substring(2) + ")";
            if (null != tianGanXiangHeMap.get(getHourGan() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年干[" + liuNianGanOrZhi + "]被时干[" + getHourGan() + "]合化(" + tianGanXiangHeMap.get(getHourGan() + liuNianGanOrZhi).substring(2) + ")";
        } else {
            // 判断四柱地支与流年地支是否构成合化
            if (null != diZhiLiuHeMap.get(getYearZhi() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年支[" + liuNianGanOrZhi + "]被年支[" + getYearZhi() + "]合化(" + diZhiLiuHeMap.get(getYearZhi() + liuNianGanOrZhi).substring(2) + ")";
            if (null != diZhiLiuHeMap.get(getMonthZhi() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年支[" + liuNianGanOrZhi + "]被月支[" + getMonthZhi() + "]合化(" + diZhiLiuHeMap.get(getMonthZhi() + liuNianGanOrZhi).substring(2) + ")";
            if (null != diZhiLiuHeMap.get(getDayZhi() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年支[" + liuNianGanOrZhi + "]被日支[" + getDayZhi() + "]合化(" + diZhiLiuHeMap.get(getDayZhi() + liuNianGanOrZhi).substring(2) + ")";
            if (null != diZhiLiuHeMap.get(getHourZhi() + liuNianGanOrZhi)) heHua = "合化_" + list.get(0) + "年：年支[" + liuNianGanOrZhi + "]被时支[" + getHourZhi() + "]合化(" + diZhiLiuHeMap.get(getHourZhi() + liuNianGanOrZhi).substring(2) + ")";
        }

        List<String> newList = new ArrayList<>(list);
        newList.add(heHua);

        return newList;

    }


}
